package com.hhkj.payment.wechat.api;

import cn.hutool.extra.servlet.ServletUtil;
import com.hhkj.payment.wechat.common.component.provider.ConfProvider;
import com.hhkj.payment.wechat.common.component.provider.ServiceProvider;
import com.hhkj.payment.wechat.common.conf.WechatConf;
import com.hhkj.payment.wechat.common.cont.WechatConst;
import com.ruoyi.common.exception.GlobalException;
import com.wechat.pay.java.core.cipher.Signer;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.core.util.NonceUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import java.time.Instant;

/**
 * WechatPayApi
 *
 * @author HHKJ-SERVER-ZJ
 * @since 2023/11/14 10:42
 */
@Slf4j
public class WechatMPPayApi {

    /**
     * 及时调用退款信息
     *
     * @param transactionId 支付订单id
     * @param outRefundNo   退款单号
     * @param refundPrice   退款价格
     * @param totalPrice    原订单价格
     * @return 退款回调信息
     */
    public static Refund refund(String transactionId, String outRefundNo, Long refundPrice, Long totalPrice) {
        if (!WechatConf.getIsPro()) {
            refundPrice = 10L;
            totalPrice = 10L;
        }
        CreateRequest createRequest = new CreateRequest();
        createRequest.setTransactionId(transactionId);
        createRequest.setOutRefundNo(outRefundNo);
        // 【金额信息】 订单金额信息
        createRequest.setNotifyUrl(WechatConf.getNotifyUrl());
        AmountReq amountReq = new AmountReq();
        amountReq.setRefund(refundPrice);
        amountReq.setTotal(totalPrice);
        amountReq.setCurrency(WechatConst.AMOUNT_CURRENCY);
        createRequest.setAmount(amountReq);
        RefundService refundService = ServiceProvider.getRefundService();
        try {
            return refundService.create(createRequest);
        } catch (ServiceException e) {
            log.error("{transactionId: " + transactionId + ",code: " + e.getErrorCode() + ", message: " + e.getErrorMessage() + " }");
            throw new GlobalException("退款出现异常，请联系客服");
        }
    }

    /**
     * 重新创建支付调用相关信息
     *
     * @param prepayId 原预支付id
     * @return 调用微信支付相关参数
     */
    public static PrepayWithRequestPaymentResponse createPrepayWithRequestPaymentResponse(String prepayId) {
        long timestamp = Instant.now().getEpochSecond();
        String nonceStr = NonceUtil.createNonce(32);
        String packageVal = "prepay_id=" + prepayId;
        String message =
                WechatConf.getMpAppId() + "\n" + timestamp + "\n" + nonceStr + "\n" + packageVal + "\n";
        log.debug("Message for RequestPayment signatures is[{}]", message);
        Signer signer = ConfProvider.getRSAAutoCertificateConfig().createSigner();
        String sign = signer.sign(message).getSign();
        PrepayWithRequestPaymentResponse response = new PrepayWithRequestPaymentResponse();
        response.setAppId(WechatConf.getMpAppId());
        response.setTimeStamp(String.valueOf(timestamp));
        response.setNonceStr(nonceStr);
        response.setPackageVal(packageVal);
        response.setSignType("RSA");
        response.setPaySign(sign);
        return response;
    }

    /**
     * 向微信服务端创建支付订单信息
     *
     * @param outTradeNo  系统订单信息
     * @param description 描述
     * @param price       总金额 说明：订单总金额，单位为分
     * @param openid      付款者openId信息
     */
    public static PrepayWithRequestPaymentResponse createPrepayWithRequestPaymentResponse(String outTradeNo, String description, Integer price, String openid) {
        JsapiServiceExtension service = ServiceProvider.getJsapiServiceExtension();

        // 跟之前下单示例一样，填充预下单参数
        PrepayRequest request = createPrepayRequest(outTradeNo, description, price, openid);

        // response包含了调起支付所需的所有参数，可直接用于前端调起支付
        try {
            return service.prepayWithRequestPayment(request);
        } catch (ServiceException e) {
            log.error("{outTradeNo: " + outTradeNo + ",code: " + e.getErrorCode() + ", message: " + e.getErrorMessage() + " }");
            throw new GlobalException("微信支付发起失败，请联系管理员");
        }
    }

    /**
     * 解析微信回调的数据信息
     *
     * @param request 请求信息
     * @return 解析完的数据实体 如果解析失败 回null
     */
    public static Transaction parserPayCallback(HttpServletRequest request) {
        String characterEncoding = request.getCharacterEncoding();
        log.info("characterEncoding=" + characterEncoding);
        //从请求头获取验签字段
        String wechatTimestamp = request.getHeader("Wechatpay-Timestamp");
        String wechatPayNonce = request.getHeader("Wechatpay-Nonce");
        String wechatSignature = request.getHeader("Wechatpay-Signature");
        String wechatPaySerial = request.getHeader("Wechatpay-Serial");

        String requestBody = ServletUtil.getBody(request);

        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatPaySerial)
                .nonce(wechatPayNonce)
                .signature(wechatSignature)
                .timestamp(wechatTimestamp)
                .body(requestBody)
                .build();

        // 如果已经初始化了 RSAAutoCertificateConfig，可直接使用
        // 没有的话，则构造一个

        // 初始化 NotificationParser
        NotificationParser parser
                = new NotificationParser((NotificationConfig) ConfProvider.getRSAAutoCertificateConfig());

        // try {
        // 以支付通知回调为例，验签、解密并转换成 Transaction
        return parser.parse(requestParam, Transaction.class);
        // } catch (ValidationException e) {
        // 签名验证失败，返回 401 UNAUTHORIZED 状态码
        // log.error("sign verification failed", e);
        // throw new GlobalException("获取");
        // }

        // 如果处理失败，应返回 4xx/5xx 的状态码，例如 500 INTERNAL_SERVER_ERROR
        // if (/* process error */) {
        // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
        // }

        // 处理成功，返回 200 OK 状态码
        // return ResponseEntity.status(HttpStatus.OK);
    }

    /**
     * 向微信服务端创建支付订单信息
     *
     * @param outTradeNo  系统订单信息
     * @param description 描述
     * @param price       总金额 说明：订单总金额，单位为分
     * @param openid      付款者openId信息
     */
    public static PrepayResponse prepay(String outTradeNo, String description, Integer price, String openid) {

        JsapiService service = ServiceProvider.getJsapiService();

        PrepayRequest prepayRequest = createPrepayRequest(outTradeNo, description, price, openid);

        return service.prepay(prepayRequest);
    }

    /**
     * 创建预支付请求
     *
     * @param outTradeNo  系统订单信息
     * @param description 描述
     * @param price       总金额 说明：订单总金额，单位为分
     * @param openid      付款者openId信息
     */
    private static PrepayRequest createPrepayRequest(String outTradeNo, String description, Integer price, String openid) {
        if (!WechatConf.getIsPro()) {
            price = 10;
        }
        PrepayRequest prepayRequest = new PrepayRequest();
        prepayRequest.setAppid(WechatConf.getMpAppId());
        prepayRequest.setMchid(WechatConf.getMchId());
        prepayRequest.setDescription(description);
        prepayRequest.setOutTradeNo(outTradeNo);
        prepayRequest.setNotifyUrl(WechatConf.getNotifyUrl());
        // 价格信息
        Amount amount = new Amount();
        amount.setCurrency(WechatConst.AMOUNT_CURRENCY);
        amount.setTotal(price);
        prepayRequest.setAmount(amount);
        // 买家信息
        Payer payer = new Payer();
        payer.setOpenid(openid);
        prepayRequest.setPayer(payer);
        return prepayRequest;
    }
}
